package com.iuhao.redis;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.DependsOn;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.concurrent.TimeUnit;

/**
 * Created by lixiangxiang on 2017/5/17.
 */
@Component(value = "o2oRedisTemplate")
@DependsOn("redisTemplate")
public class O2ORedisTemplate {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    private final ObjectMapper objectMapper = new ObjectMapper();

    /**
     * 默认过期时间为10天
     */
    private final int defailtExpireTime = 10 * 24 * 60 * 60;

    private final Logger logger = LoggerFactory.getLogger(O2ORedisTemplate.class);

    public void put(final String key, final Object value, final int seconds) {

        redisTemplate.execute(new RedisCallback() {
            @Override
            public Long doInRedis(RedisConnection connection) throws DataAccessException {
                try {
                    connection.set(key.getBytes(), objectMapper.writeValueAsBytes(value));
                } catch (JsonProcessingException e) {
                    logger.error("O2ORedisTemplate.put(): " + e.getMessage());
                }
                connection.expire(redisTemplate.getStringSerializer().serialize(key), seconds > 0 ? seconds : defailtExpireTime);
                return 1L;
            }
        });
    }

    public void put(final String key, final Object value, final int seconds, final boolean forever) {

        redisTemplate.execute(new RedisCallback() {
            @Override
            public Long doInRedis(RedisConnection connection) throws DataAccessException {
                try {
                    connection.set(key.getBytes(), objectMapper.writeValueAsBytes(value));
                } catch (JsonProcessingException e) {
                    logger.error("O2ORedisTemplate.put(): " + e.getMessage());
                }
                if (!forever) {
                    connection.expire(redisTemplate.getStringSerializer().serialize(key), seconds > 0 ? seconds : defailtExpireTime);
                }
                return 1L;
            }
        });
    }

    public void put(final String key, final Object value) {
        this.put(key, value, defailtExpireTime);
    }

    public <T> T get(final String key, final Class<T> clazz) {
        if (StringUtils.isEmpty(key)) {
            return null;
        }
        return redisTemplate.execute(new RedisCallback<T>() {
            @Override
            public T doInRedis(RedisConnection connection) throws DataAccessException {
                if (connection.exists(key.getBytes())) {
                    byte[] value = connection.get(key.getBytes());
                    try {
                        return objectMapper.readValue(value, clazz);
                    } catch (IOException e) {
                        logger.error("O2ORedisTemplate.get(): " + e.getMessage());
                    }
                }
                return null;
            }
        });
    }

    public String get(final String key) {
        return get(key, String.class);
    }

    public void delete(final String... keys) {
        redisTemplate.execute(new RedisCallback() {
            @Override
            public Long doInRedis(RedisConnection connection) throws DataAccessException {
                long result = 0;
                for (int i = 0; i < keys.length; i++) {
                    result = connection.del(keys[i].getBytes());
                }
                return result;
            }
        });
    }

    /**
     * @param key
     * @param t
     * @param seconds
     * @param <T>
     */
    public <T> void setOpsForValue(final String key, T t, final int seconds) {
        redisTemplate.opsForValue().set(key, t, seconds > 0 ? seconds : defailtExpireTime, TimeUnit.SECONDS);
    }

    /**
     * @param key
     * @return
     */
    public Object getOpsForValue(final String key) {
        return redisTemplate.opsForValue().get(key);
    }

    /**
     * @param key
     * @return
     */
    public boolean isContinueKey(final String key) {
        long count = redisTemplate.opsForValue().increment(key, 1);
        boolean isContinue = false;
        if (count <= 1) {
            setExpire(key, 60);
            isContinue = true;
        }
        return isContinue;
    }
    //银行卡防止同一卡多次认证
    public boolean isContinueBankCardKey(final String key) {
        long count = redisTemplate.opsForValue().increment(key, 1);
        boolean isContinue = false;
        if (count <= 4) {
            setExpire(key, 60);
            isContinue = true;
        }
        return isContinue;
    }
    /**
     * @param key
     */
    public void setExpire(final String key, int timeout) {
        redisTemplate.expire(key, timeout, TimeUnit.SECONDS);
    }
}
